import math
from math import sqrt
import numbers


def zeroes(height, width):
    '''
    :param height: 矩阵的高
    :param width: 矩阵的宽
    '''

    # g:一个list类型的矩阵列表
    g = [[0.0 for _ in range(width)] for __ in range(height)]
    return Matrix(g)


def ones(height, width):
    '''
    :param height: 矩阵的高
    :param width: 矩阵的宽
    :return: Matrix实例
    '''

    # g:一个list类型的矩阵列表
    g = [[1 for _ in range(width)] for __ in range(height)]
    return Matrix(g)


def identity(n):
    '''

    :param n: 方阵的高和宽
    :return: 一个高和宽为n的单位矩阵
    '''
    """
    Creates a n x n identity matrix.
    """
    I = zeroes(n, n)
    for i in range(n):
        I.g[i][i] = 1.0
    return I


class Matrix(object):
    def __init__(self, grid):
        self.g = grid
        self.h = len(grid)
        self.w = len(grid[0])

    def determinant(self):
        if not self.is_square():
            raise (ValueError, "Cannot calculate determinant of non-square matrix.")
        if self.h > 2:
            raise (NotImplementedError, "Calculating determinant not implemented for matrices largerer than 2x2.")

        if self.h == 1:
            return self.g[0][0]

        if self.h == 2:
            return self.g[0][0] * self.g[1][1] - self.g[0][1] * self.g[1][0]

    def trace(self):
        if not self.is_square():
            raise (ValueError, "Cannot calculate the trace of a non-square matrix.")

        res = 0
        for point in range(self.h):
            res += self.g[point][point]
        return res

    def inverse(self):
        if not self.is_square():
            raise (ValueError, "Non-square Matrix does not have an inverse.")
        if self.h > 2:
            raise (NotImplementedError, "inversion not implemented for matrices larger than 2x2.")

        if self.w == 1:
            if self.g[0][0] == 0:
                raise ValueError('该矩阵无逆矩阵')
            else:
                return 1 / self.g[0][0]

        if self.w == 2:
            if self.determinant() == 0:
                raise ValueError('该矩阵无逆矩阵')
            else:
                return 1 / self.determinant() * (self.trace() * identity(self.w) - self)

    def T(self):
        grid = zeroes(self.w, self.h)
        for r in range(self.h):
            for c in range(self.w):
                grid[c][r] = self.g[r][c]
        return grid

    def is_square(self):
        return self.h == self.w

    def __getitem__(self, idx):
        return self.g[idx]

    def __repr__(self):
        s = ""
        for row in self.g:
            s += " ".join(["{} ".format(x) for x in row])
            s += "\n"
        return s

    def __add__(self, other):
        if self.h != other.h or self.w != other.w:
            raise (ValueError, "Matrices can only be added if the dimensions are the same")
        matrix = zeroes(self.h, self.w)
        for r in range(self.h):
            for c in range(self.w):
                matrix[r][c] = self.g[r][c] + other.g[r][c]
        return matrix

    def __neg__(self):
        matrix = zeroes(self.h, self.w)
        for r in range(self.h):
            for c in range(self.w):
                matrix[r][c] = -self.g[r][c]
        return matrix

    def __sub__(self, other):
        matrix = zeroes(self.h, self.w)
        for r in range(self.h):
            for c in range(self.w):
                matrix[r][c] = self.g[r][c] - other.g[r][c]
        return matrix

    def get_col(self, matrix, col):
        if col > matrix.w:
            raise IndexError('索引超出范围')
        res = []
        for i in matrix.g:
            res.append(i[col])
        return res

    def __mul__(self, other):
        if self.w != other.h:
            raise KeyError('X*Y需要,X的行数等于Y的列数')

        def cal_item(l1, l2):
            res = 0
            for i in range(len(l1)):
                t = l1[i] * l2[i]
                res += t
            return res

        res = []
        for i, row in enumerate(self.g):
            temp = []
            for j in range(other.w):
                temp.append(cal_item(row, self.get_col(other, j)))
            res.append(temp)

        return Matrix(res)

    def __rmul__(self, other):
        if isinstance(other, numbers.Number):
            matrix = zeroes(self.h, self.w)
            for r in range(self.h):
                for c in range(self.w):
                    matrix[r][c] = self.g[r][c] * other
            return matrix

if __name__ == '__main__':
    m1 = Matrix([
        [1,2,3],
        [4,5,6]
        ])

    m2 = Matrix([
        [7,-2],
        [-3,-5],
        [4,1]
        ])
    print(m1*m2)